package com.google.code.jerseyclients;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import javax.ws.rs.core.MediaType;

import junit.framework.Assert;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.servlet.GuiceFilter;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse.Status;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.WebResource.Builder;
import com.sun.jersey.spi.container.servlet.ServletContainer;

/**
 * @author <a href="mailto:olamy@apache.org">Olivier Lamy</a>
 */
@RunWith(JUnit4.class)
public abstract class AbstractBeerServiceJerseyCall
{

    private Logger log = LoggerFactory.getLogger( getClass() );

    Server server = null;

    int port;

    JerseyClientBuilder clientBuilder;

    JerseyHttpClientConfig jerseyHttpClientConfig;
    
    Client client;

    @Before
    public void startServer()
        throws Exception
    {
        ServletHolder sh = new ServletHolder( ServletContainer.class );
        sh.setInitParameter( "com.sun.jersey.config.property.resourceConfigClass",
                             "com.sun.jersey.api.core.PackagesResourceConfig" );
        sh.setInitParameter( "com.sun.jersey.config.property.packages", "com.google.code.jerseyclients" );
        this.server = new Server( 0 );

        Context context = new Context( server, "/", Context.SESSIONS );

        context.addEventListener( new GuiceServletConfig() );

        context.addFilter( GuiceFilter.class, "/*", 1 );
        context.addServlet( sh, "/*" );
        this.server.start();
        Connector connector = this.server.getConnectors()[0];
        this.port = connector.getLocalPort();
        log.info( "start server on port " + this.port );

        this.jerseyHttpClientConfig = new DefaultJerseyHttpClientConfig().setMaxConnections( 10 )
            .setMaxConnectionsPerHost( 10 ).setConnectionStaleCheckingEnabled( true ).setConnectionTimeOut( 10000 )
            .setReadTimeOut( 10000 );

        clientBuilder = new JerseyClientBuilder();
        clientBuilder.initManagerPerProvider( getImpl(), this.jerseyHttpClientConfig );
        
        client = clientBuilder.build( "", this.jerseyHttpClientConfig, getImpl() );
    }

    @After
    public void stopServer()
        throws Exception
    {
        if ( this.server != null && this.server.isRunning() )
        {
            this.server.stop();
        }
    }

    public abstract short getImpl();

    @Test
    public void getAllBeersXml()
    {
        getAllBeers( MediaType.APPLICATION_XML );

    }

    @Test
    public void searchBeerXml()
    {
        searchBeer( MediaType.APPLICATION_XML );
    }
    
    @Test
    public void getAllBeersJson()
    {
        getAllBeers( MediaType.APPLICATION_JSON );

    }

    @Test
    public void searchBeerJson()
    {
        searchBeer( MediaType.APPLICATION_JSON );
    } 

    @Test
    public void searchWithPostXml()
    {
        searchWithPost( MediaType.APPLICATION_XML );
    }
    
    @Test
    public void searchWithPostJson()
    {
        searchWithPost( MediaType.APPLICATION_JSON );
    }
    
    @Test
    public void searchWithPostReturnNullXml()
    {
        try
        {
            searchWithPostReturnNull( MediaType.APPLICATION_XML );
        }
        catch ( UniformInterfaceException e )
        {
            Assert.assertEquals( Status.NO_CONTENT.getStatusCode(), e.getResponse().getStatus() );
        }
    }

    @Test
    public void searchWithPostReturnNullJson()
    {
        try
        {
            searchWithPostReturnNull( MediaType.APPLICATION_JSON );
            Assert.fail( "not in UniformInterfaceException" );
        }
        catch ( UniformInterfaceException e )
        {
            Assert.assertEquals( Status.NO_CONTENT.getStatusCode(), e.getResponse().getStatus() );
        }
    }   
    

    public void searchWithPost(String type)
    {
        WebResource webResource =  client.resource( "http://localhost:" + this.port ).path( "/beerService/searchBeerWithRequest" );
        webResource.accept( type );
        BeerSearchRequest beerSearchRequest = new BeerSearchRequest( "HEIK" );
        Builder builder = webResource.entity( beerSearchRequest, type );
        Beer beer = builder.post( Beer.class );
        Assert.assertNotNull( beer );
        Assert.assertEquals( "Heinekein", beer.getName() );
    }
    
    public void searchWithPostReturnNull(String type)
    {
        WebResource webResource = client.resource( "http://localhost:" + this.port ).path( "/beerService/searchBeerWithRequest" );
        webResource.accept( type );
        BeerSearchRequest beerSearchRequest = new BeerSearchRequest( "foo" );
        Builder builder = webResource.entity( beerSearchRequest, type );
        Beer beer = builder.post( Beer.class );
        Assert.assertNull( beer );
    }    

    public void getAllBeers( String type )
    {
        BeerList list = client.resource( "http://localhost:" + this.port + "/beerService/getAllBeers" ).accept( type )
            .get( BeerList.class );
        Assert.assertNotNull( list );
        Assert.assertEquals( 2, list.getBeers().size() );

    }

    public void searchBeer( String type )
    {
        Beer beer = client.resource( "http://localhost:" + this.port ).path( "/beerService/searchBeer" )
            .queryParam( "code", "AFFL" ).accept( type ).get( Beer.class );
        Assert.assertNotNull( beer );
        Assert.assertEquals( "Haffligem", beer.getName() );
    }

}
